﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

using System.Data.SqlClient;
using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Globalization;

namespace GPS.COMMON
{

    /// <summary>
    /// GiangTD3 -2017-09-10: Lam viec voi Week Date:
    /// </summary>
    /// 
    public class WeekDate : Week
    {
        private readonly DateTime _date;

        public WeekDate(DateTime date)
            : base(date)
        {
            _date = date;
            DayNumber = GetDayNumber(date.DayOfWeek);
        }

        public int DayNumber
        {
            get;
            private set;
        }

        /// <summary>
        /// Returns a sortable ISO week date string (e.g. "2009-W53-2").
        /// </summary>
        public override string ToString()
        {
            return string.Format("{0}-W{1:00}-{2}", WeekYear, WeekNumber, DayNumber);
        }

        private int GetDayNumber(DayOfWeek dayOfWeek)
        {
            switch (dayOfWeek)
            {
                case DayOfWeek.Monday:
                    return 1;
                case DayOfWeek.Tuesday:
                    return 2;
                case DayOfWeek.Wednesday:
                    return 3;
                case DayOfWeek.Thursday:
                    return 4;
                case DayOfWeek.Friday:
                    return 5;
                case DayOfWeek.Saturday:
                    return 6;
                case DayOfWeek.Sunday:
                    return 7;
                default:
                    throw new ArgumentOutOfRangeException("dayOfWeek", dayOfWeek, "Unknown day of week.");
            }
        }

        public static IList<WeekDate> GetWeekDates(DateTime from, DateTime to)
        {
            List<WeekDate> weekDates = new List<WeekDate>();
            weekDates.Add(new WeekDate(from));
            while (weekDates.Last()._date < to.Date)
            {
                DateTime nextDay = weekDates.Last()._date.AddDays(1);
                weekDates.Add(new WeekDate(nextDay));
            }
            return weekDates;
        }
    }
    public class Week : IComparable<Week>, IEquatable<Week>
    {
        private const DayOfWeek FirstDayOfWeek = DayOfWeek.Monday;
        private const DayOfWeek LastDayOfWeek = DayOfWeek.Sunday;
        private const DayOfWeek PivotDayOfWeek = DayOfWeek.Thursday;
        private static readonly System.Globalization.Calendar Calendar = CultureInfo.InvariantCulture.Calendar;

        public Week(DateTime date)
        {
            StartOfWeek = GetStartOfWeek(date);
            EndOfWeek = GetEndOfWeek(date);
            DateTime pivotDayOfWeek = GetPivotDayOfWeek(StartOfWeek);
            WeekNumber = Calendar.GetWeekOfYear(pivotDayOfWeek, CalendarWeekRule.FirstFourDayWeek, FirstDayOfWeek);
            WeekYear = pivotDayOfWeek.Year;
        }

        public int MonthOfWeek
        {
            get
            {
                var mothofweek = GPS_Library.GetMonth(this.WeekYear, this.WeekNumber);
                return mothofweek;
            }
        }

        public int MonthOfWeek_CompareDetal
        {
            get
            {
                var mothofweek = 0;
                double _deltaStart = (DateTimeExtensions.LastDayOfMonth(this.StartOfWeek) - this.StartOfWeek).TotalDays; //(EndDate - StartDate).TotalDays
                double _deltaEnd = (this.EndOfWeek - DateTimeExtensions.LastDayOfMonth(this.StartOfWeek)).TotalDays; //(EndDate - StartDate).TotalDays
                if (_deltaEnd >= 0)
                {
                    if (_deltaEnd > _deltaStart)
                        mothofweek = this.EndOfWeek.Month;
                    else
                        mothofweek = this.StartOfWeek.Month;
                }
                else
                {
                    mothofweek = this.StartOfWeek.Month;
                }

                return mothofweek;
            }
        }

        public DateTime StartOfWeek
        {
            get;
            private set;
        }

        public DateTime EndOfWeek
        {
            get;
            private set;
        }

        public int WeekNumber
        {
            get;
            private set;
        }

        public int WeekYear
        {
            get;
            private set;
        }

        private DateTime GetStartOfWeek(DateTime date)
        {
            while (date.DayOfWeek != FirstDayOfWeek)
            {
                date = date.AddDays(-1);
            }
            return date;
        }

        private DateTime GetEndOfWeek(DateTime date)
        {
            while (date.DayOfWeek != LastDayOfWeek)
            {
                date = date.AddDays(1);
            }
            return date;
        }

        private DateTime GetPivotDayOfWeek(DateTime startOfWeek)
        {
            while (startOfWeek.DayOfWeek != PivotDayOfWeek)
            {
                startOfWeek = startOfWeek.AddDays(1);
            }
            return startOfWeek;
        }

        /// <summary>
        /// Returns a sortable ISO week string (e.g. "2009-W53").
        /// </summary>
        public override string ToString()
        {
            return string.Format("{0}-W{1:00}", WeekYear, WeekNumber);
        }

        public int CompareTo(Week other)
        {
            return other == null ? -1 : string.Compare(ToString(), other.ToString(), StringComparison.Ordinal);
        }

        public bool Equals(Week other)
        {
            return other != null && string.Equals(ToString(), other.ToString(), StringComparison.Ordinal);
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as Week);
        }

        public override int GetHashCode()
        {
            return ToString().GetHashCode();
        }

        public static IList<Week> GetWeeks(DateTime from, DateTime to)
        {
            List<Week> weeks = new List<Week>();
            weeks.Add(new Week(from));
            while (weeks.Last().EndOfWeek.Date < to.Date)
            {
                DateTime startOfNextWeek = weeks.Last().EndOfWeek.AddDays(1);
                weeks.Add(new Week(startOfNextWeek));
            }
            return weeks;
        }
    }

    public class DateRange
    {
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }

        public string StrStartDate
        {
            get
            {
                return StartDate.ToString("dd/MM/yyyy");
            }
        }

        public string StrEndDate
        {
            get
            {
                return EndDate.ToString("dd/MM/yyyy");
            }
        }

        public string Thang { get; set; }

    }

    public static partial class DateTimeExtensions
    {
        // Always uses Monday-to-Sunday weeks
        public static DateTime GetStartOfWeek(DateTime input)
        {
            // Using +6 here leaves Monday as 0, Tuesday as 1 etc.
            var i1 = (int)input.DayOfWeek;
            var i2 = (i1) + 6;
            int dayOfWeek = (i2) % 7;
            return input.Date.AddDays(-dayOfWeek);
        }

        public static int GetWeeks(DateTime start, DateTime end)
        {
            start = GetStartOfWeek(start);
            end = GetStartOfWeek(end);
            int days = (int)(end - start).TotalDays;
            return (days / 7) + 1; // Adding 1 to be inclusive
        }
        public static int NumberOfWeeks(DateTime dateFrom, DateTime dateTo)
        {
            TimeSpan Span = dateTo.Subtract(dateFrom);

            if (Span.Days <= 7)
            {
                if (dateFrom.DayOfWeek > dateTo.DayOfWeek)
                {
                    return 2;
                }

                return 1;
            }

            int Days = Span.Days - 7 + (int)dateFrom.DayOfWeek;
            int WeekCount = 1;
            int DayCount = 0;

            for (WeekCount = 1; DayCount < Days; WeekCount++)
            {
                DayCount += 7;
            }

            return WeekCount;
        }

        public static DateTime FirstDayOfWeek(this DateTime dt)
        {
            var culture = System.Threading.Thread.CurrentThread.CurrentCulture;
            var diff = dt.DayOfWeek - culture.DateTimeFormat.FirstDayOfWeek;
            if (diff < 0)
                diff += 7;
            return dt.AddDays(-diff).Date;
        }

        //public static DateTime LastDayOfWeek(this DateTime dt)
        //{
        //    return dt.FirstDayOfWeek().AddDays(6);
        //}

        public static DateTime FirstDayOfMonth(DateTime dt)
        {
            return new DateTime(dt.Year, dt.Month, 1);
        }

        public static DateTime LastDayOfMonth(DateTime dt)
        {
            return FirstDayOfMonth(dt).AddMonths(1).AddDays(-1);
        }

        //public static DateTime FirstDayOfNextMonth(this DateTime dt)
        //{
        //    return dt.FirstDayOfMonth().AddMonths(1);
        //}

    }
    public class GPS_Library
    {
        public static string Get_ddMM(DateTime date)
        {
            var str = date.ToString("dd/MM/yyyy");

            var str1 = str.Substring(0, 5);
            return str1;
        }
        public static int GetMonth(int Year, int Week)
        {
            DateTime tDt = new DateTime(Year, 1, 1);

            tDt.AddDays((Week - 1) * 7);

            for (int i = 0; i <= 365; ++i)
            {
                int tWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(tDt, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
                if (tWeek == Week)
                    return tDt.Month;

                tDt = tDt.AddDays(1);
            }
            return 0;
        }
        /// <summary>
        /// Get the last date of the week for a certain date, provided
        /// that the first day of the week is Monday, the first week of
        /// a year is the one that includes the first Thursday of that
        /// year and the last week of a year is the one that immediately
        /// precedes the first calendar week of the next year.
        /// </summary>
        /// <param name="date">ISO 8601 date of interest.</param>
        /// <returns>The first week date.</returns>
        public static DateTime GetLastDateOfWeek(DateTime date)
        {
            if (date == DateTime.MaxValue)
            {
                return date;
            }

            int week = GetWeekNumber(date);

            while (week == GetWeekNumber(date))
            {
                date = date.AddDays(1);
            }

            return date.AddDays(-1);
        }

        /// <summary>
        /// Get the first date of the week for a certain date, provided
        /// that the first day of the week is Monday, the first week of
        /// a year is the one that includes the first Thursday of that
        /// year and the last week of a year is the one that immediately
        /// precedes the first calendar week of the next year.
        /// </summary>
        /// <param name="date">ISO 8601 date of interest.</param>
        /// <returns>The first week date.</returns>
        public static DateTime GetFirstDateOfWeek(DateTime date)
        {
            if (date == DateTime.MinValue)
            {
                return date;
            }

            int week = GetWeekNumber(date);

            while (week == GetWeekNumber(date))
            {
                date = date.AddDays(-1);
            }

            return date.AddDays(1);
        }
        public static int GetWeekNumber(DateTime date)
        {
            //Constants
            const int JAN = 1;
            const int DEC = 12;
            const int LASTDAYOFDEC = 31;
            const int FIRSTDAYOFJAN = 1;
            const int THURSDAY = 4;
            bool thursdayFlag = false;

            //Get the day number since the beginning of the year
            int dayOfYear = date.DayOfYear;

            //Get the first and last weekday of the year
            int startWeekDay = (int)(new DateTime(date.Year, JAN, FIRSTDAYOFJAN)).DayOfWeek;
            int endWeekDay = (int)(new DateTime(date.Year, DEC, LASTDAYOFDEC)).DayOfWeek;

            //Compensate for using monday as the first day of the week
            if (startWeekDay == 0)
            {
                startWeekDay = 7;
            }
            if (endWeekDay == 0)
            {
                endWeekDay = 7;
            }

            //Calculate the number of days in the first week
            int daysInFirstWeek = 8 - (startWeekDay);

            //Year starting and ending on a thursday will have 53 weeks
            if (startWeekDay == THURSDAY || endWeekDay == THURSDAY)
            {
                thursdayFlag = true;
            }

            //We begin by calculating the number of FULL weeks between
            //the year start and our date. The number is rounded up so
            //the smallest possible value is 0.
            int fullWeeks = (int)Math.Ceiling((dayOfYear - (daysInFirstWeek)) / 7.0);
            int result = fullWeeks;

            //If the first week of the year has at least four days, the
            //actual week number for our date can be incremented by one.
            if (daysInFirstWeek >= THURSDAY)
            {
                result = result + 1;
            }

            //If the week number is larger than 52 (and the year doesn't
            //start or end on a thursday), the correct week number is 1.
            if (result > 52 && !thursdayFlag)
            {
                result = 1;
            }

            //If the week number is still 0, it means that we are trying
            //to evaluate the week number for a week that belongs to the
            //previous year (since it has 3 days or less in this year).
            //We therefore execute this function recursively, using the
            //last day of the previous year.
            if (result == 0)
            {
                result = GetWeekNumber(new DateTime(date.Year - 1, DEC, LASTDAYOFDEC));
            }

            return result;
        }

        public static Dictionary<int, DateRange> GET_WEEK_DATERANGE_WORK(DateTime startDate, DateTime endDate)
        {
            Dictionary<int, DateRange> dictWeekAndDate = new Dictionary<int, DateRange>();
            var start1 = startDate;
            DateRange daterange = null;
            var k = 1;
            while (start1 <= endDate)
            {
                daterange = new DateRange();
                var end = DateTimeExtensions.GetStartOfWeek(start1.AddDays(7)).AddDays(-1);
                if (end > endDate)
                    end = endDate;

                daterange.StartDate = start1;
                if (start1.DayOfWeek == DayOfWeek.Saturday)
                {
                    daterange.StartDate = start1.AddDays(2);
                }

                if (start1.DayOfWeek == DayOfWeek.Sunday)
                {
                    daterange.StartDate = start1.AddDays(1);
                }
                daterange.EndDate = end;
                if (end.DayOfWeek == DayOfWeek.Saturday)
                {
                    daterange.EndDate = end.AddDays(-1);
                }

                if (end.DayOfWeek == DayOfWeek.Sunday)
                {
                    daterange.EndDate = end.AddDays(-2);
                }

                double _deltaStart = (DateTimeExtensions.LastDayOfMonth(start1) - start1).TotalDays; //(EndDate - StartDate).TotalDays
                double _deltaEnd = (end - DateTimeExtensions.LastDayOfMonth(start1)).TotalDays; //(EndDate - StartDate).TotalDays
                if (_deltaEnd >= 0)
                {
                    if (_deltaEnd > _deltaStart)
                        daterange.Thang = end.Month.ToString();
                    else
                        daterange.Thang = start1.Month.ToString();
                }
                else
                {
                    daterange.Thang = start1.Month.ToString();
                }

                dictWeekAndDate.Add(k, daterange);
                start1 = end.AddDays(1);
                k++;
            }

            return dictWeekAndDate;
        }

        public static Dictionary<int, DateRange> GET_WEEK_DATERANGE(DateTime startDate, DateTime endDate)
        {
            Dictionary<int, DateRange> dictWeekAndDate = new Dictionary<int, DateRange>();
            var start1 = startDate;
            DateRange daterange = null;
            var k = 1;
            while (start1 <= endDate)
            {
                daterange = new DateRange();
                var end = DateTimeExtensions.GetStartOfWeek(start1.AddDays(7)).AddDays(-1);
                if (end > endDate)
                    end = endDate;

                daterange.StartDate = start1;
                daterange.EndDate = end;
                double _deltaStart = (DateTimeExtensions.LastDayOfMonth(start1) - start1).TotalDays; //(EndDate - StartDate).TotalDays
                double _deltaEnd = (end - DateTimeExtensions.LastDayOfMonth(start1)).TotalDays; //(EndDate - StartDate).TotalDays
                if (_deltaEnd >= 0)
                {
                    if (_deltaEnd > _deltaStart)
                        daterange.Thang = end.Month.ToString();
                    else
                        daterange.Thang = start1.Month.ToString();
                }
                else
                {
                    daterange.Thang = start1.Month.ToString();
                }

                dictWeekAndDate.Add(k, daterange);
                start1 = end.AddDays(1);
                k++;
            }

            return dictWeekAndDate;
        }

        #region "DataTable Extend"

        //public DataTable ToDataTable<T>(IList<T> lst)
        //{
        //    //'//Tạo một DataTable từ T
        //    DataTable table = this.CreateTable<T>();
        //    //'Lấy danh sách Property của T
        //    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        //    //'Lặp qua IList , gán giá trị của các thuộc tính có tên = tên cột trong Datatable vừa tạo
        //    foreach (T item in lst)
        //    {
        //        //'Tạo một row mới 
        //        DataRow row = table.NewRow();
        //        //'Duyệt qua Property, đồng thời gán giá trị cho column
        //        foreach (PropertyDescriptor prop in properties)
        //        {
        //            table.Columns.Cast<DataColumn>().ToList().ForEach(c =>
        //            {
        //                if (c.ColumnName == prop.Name)
        //                {
        //                    row[c.ColumnName] = prop.GetValue(item) ?? DBNull.Value;

        //                }
        //            });
        //        }
        //        //'thêm mới một row vào table
        //        table.Rows.Add(row: row);
        //    }
        //    return table;
        //}


        #endregion

        #region "List and Object Extend"
        public static List<T> MapDataTableToList<T>(DataTable dt)
        {
            if (dt == null)
            {
                return null;
            }

            List<T> list = new List<T>();

            foreach (DataRow dr in dt.Rows)
            {
                list.Add(MapDataRowToObject<T>(dr));
            }

            return list;
        }

        public static T MapDataRowToObject<T>(DataRow dr)
        {
            if (dr == null)
            {
                return default(T);
            }

            T instance = Activator.CreateInstance<T>();

            PropertyInfo[] properties = instance.GetType().GetProperties();

            if ((properties.Length > 0))
            {

                foreach (PropertyInfo propertyObject in properties)
                {
                    bool valueSet = false;


                    foreach (object attributeObject in propertyObject.GetCustomAttributes(false))
                    {
                        if (object.ReferenceEquals(attributeObject.GetType(), typeof(MapperColumn)))
                        {
                            MapperColumn columnAttributeObject = (MapperColumn)attributeObject;


                            if ((columnAttributeObject.ColumnName != string.Empty))
                            {
                                //Type t = Nullable.GetUnderlyingType(propertyObject.PropertyType) ?? targetObj.PropertyType;

                                //object safeValue = (p.GetValue(sourceObject, null) == null) ? null : Convert.ChangeType(p.GetValue(sourceObject, null), t);


                                if (dr.Table.Columns.Contains(columnAttributeObject.ColumnName) && (!object.ReferenceEquals(dr[columnAttributeObject.ColumnName], DBNull.Value)))
                                {
                                    propertyObject.SetValue(instance, dr[columnAttributeObject.ColumnName], null);

                                    valueSet = true;

                                }

                            }
                        }
                    }

                    if (!valueSet)
                    {
                        if (dr.Table.Columns.Contains(propertyObject.Name) && !object.ReferenceEquals(dr[propertyObject.Name], DBNull.Value))
                        {
                            propertyObject.SetValue(instance, dr[propertyObject.Name], null);

                        }
                    }

                }
            }

            return instance;
        }

        public static List<T> ToList<T>(params T[] arrT)
        {
            return new List<T>((T[])arrT);
        }


        #endregion

        #region "Enum"
        public enum ParameterDirect
        {
            Input = 1,
            InOutPut = 2,
            OutPut = 3,
            ReturnValue = 4
        }

        public enum DataProvider
        {
            SqlServer = 1,
            OleDb = 2,
            Odbc = 3,
            MySql = 4,
            Oracle = 5
        }


        public enum GTTSqlType
        {
            GTTDecimal = SqlDbType.Decimal,
            GTTBigInt = SqlDbType.BigInt,
            GTTBit = SqlDbType.Bit,
            GTTInt = SqlDbType.Int,
            GTTSmallInt = SqlDbType.SmallInt,
            GTTReal = SqlDbType.Real,
            GTTDate = SqlDbType.Date,
            GTTDateTime = SqlDbType.DateTime,
            //GTTLong = SqlDbType.Float
            GTTNVarchar = SqlDbType.NVarChar,
            GTTVarchar = SqlDbType.VarChar,
            GTTFloat = SqlDbType.Float
        }

        #endregion

        #region "Properties"
        [AttributeUsage(AttributeTargets.Property)]
        private class MapperColumn : Attribute
        {


            private string mColumnName;
            public MapperColumn(string columnName)
            {
                mColumnName = columnName;
            }

            public string ColumnName
            {
                get { return mColumnName; }
                set { mColumnName = value; }
            }
        }
        #endregion
    }
}
